STM32 您所在的位置:网站首页 stm32 spi nss STM32

STM32

2024-07-14 12:29| 来源: 网络整理| 查看: 265

1024G 嵌入式资源大放送!包括但不限于C/C++、单片机、Linux等。关注微信公众号【嵌入式大杂烩】,回复1024,即可免费获取!

例子说明及框图

本例子基于STM32F103ZET6芯片,实现SPI1与SPI2的主从通信。其中SPI1配置为主机,SPI2配置为从机,均配置为全双工模式。硬件连接图:

======001

其中,我们需要注意的是,SPI的从机不能主动发送数据,只能应答数据。本例子的数据交互过程:

主机使用查询方式发送数据给从机。从机使用中断接收方式接收数据,把接收到的数据加上0x05再发送给主机。

从机总是在收到主机的数据时,才会发送数据给从机。即从机被动发送数据,也即主机主动申请数据。

代码细节

主函数:

int main(void) { uint8_t i = 0; //----------------------------------------------------------------------------------------------- // 上电初始化函数 SysInit(); //----------------------------------------------------------------------------------------------- // 主程序 while (1) { /* 主机发、收数据 */ for (i = 0; i < SPI_BUF_LEN; i++) { ucSPI1_RxBuf[i] = SPI1_ReadWriteByte(ucSPI1_TxBuf[i]); } } return 0; }

其中,ucSPI1_RxBuf与ucSPI1_TxBuf的定义为:

uint8_t ucSPI1_RxBuf[SPI_BUF_LEN] = {0}; uint8_t ucSPI1_TxBuf[SPI_BUF_LEN] = {0x01, 0x02, 0x03, 0x04, 0x05};

SPI1_ReadWriteByte函数为SPI1的读写函数,其作用是往SPI1发送缓冲区写入数据的同时可以读取SPI1接收缓冲区中的数据,其内部实现为:

uint8_t SPI1_ReadWriteByte(uint8_t TxData) { while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); // 等待发送区空 SPI_I2S_SendData(SPI1, TxData); // 通过外设SPI1发送一个byte数据 while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);// 等待接收完一个byte return SPI_I2S_ReceiveData(SPI1); // 返回通过SPIx最近接收的数据 }

为什么可以这么写呢?看一下SPI的框图:

======002

从框图可看出SPI有 2 个缓冲区,一个用于写入(发送缓冲区),一个用于读取(接收缓冲区)。对数据寄存器执行写操作时,数据将写入发送缓冲区,从数据寄存器执行读取时,将返回接收缓冲区中的值。这样写并不会出现读到的数据等于发送的数据。

SPI2中断函数:

void SPI2_IRQHandler(void) { /* 判断接收缓冲区是否为非空 */ if (SET == SPI_I2S_GetITStatus(SPI2, SPI_I2S_IT_RXNE)) { ucSPI2_RxBuf[ucSPI2_RxCount] = SPI2->DR; /* 读取接收缓冲区数据 */ while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET); /* 等待发送区空 */ SPI2->DR = ucSPI2_RxBuf[ucSPI2_RxCount] + 0x05; /* 往发送缓冲区填数据 */ /* 计数器处理 */ ucSPI2_RxCount++; if (ucSPI2_RxCount > SPI_BUF_LEN - 1) { ucSPI2_RxCount = 0; } /* 清中断标志 */ SPI_I2S_ClearITPendingBit(SPI2, SPI_I2S_IT_RXNE); } }

从机接收到主机数据后,会加上0x05,再返还给主机。

SPI1初始化函数:

void bsp_SPI1_Init(void) { /* 定义SPI结构体变量 */ GPIO_InitTypeDef GPIO_InitStructure; SPI_InitTypeDef SPI_InitStructure; /* SPI的IO口和SPI外设打开时钟 */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); /* SPI的IO口设置 */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); /* SPI的基本配置 */ SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; // 设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工 SPI_InitStructure.SPI_Mode = SPI_Mode_Master; // 设置SPI工作模式:设置为主SPI SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // 设置SPI的数据大小:SPI发送接收8位帧结构 SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; // 串行同步时钟的空闲状态为高电平 SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; // 串行同步时钟的第二个跳变沿(上升或下降)数据被采样 SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; // NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制 SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;// 定义波特率预分频的值:波特率预分频值为256 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;// 指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始 SPI_InitStructure.SPI_CRCPolynomial = 7; // CRC值计算的多项式 SPI_Init(SPI1, &SPI_InitStructure); // 根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器 SPI_Cmd(SPI1, ENABLE); // 使能SPI外设 }

SPI1配置为主模式,全双工。

SPI2初始化函数:

void bsp_SPI2_Init(void) { /* 定义SPI结构体变量 */ GPIO_InitTypeDef GPIO_InitStructure; SPI_InitTypeDef SPI_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; /* SPI的IO口和SPI外设打开时钟 */ RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE ); RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE); /* SPI的IO口设置 */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //PB13/14/15复用推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); /* SPI的基本配置 */ SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; // 设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工 SPI_InitStructure.SPI_Mode = SPI_Mode_Slave; // 设置SPI工作模式:设置为从SPI SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // 设置SPI的数据大小:SPI发送接收8位帧结构 SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; // 串行同步时钟的空闲状态为高电平 SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; // 串行同步时钟的第二个跳变沿(上升或下降)数据被采样 SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; // NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制 SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;// 定义波特率预分频的值:波特率预分频值为256 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;// 指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始 SPI_InitStructure.SPI_CRCPolynomial = 7; // CRC值计算的多项式 SPI_Init(SPI2, &SPI_InitStructure); // 根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器 SPI_I2S_ITConfig(SPI2, SPI_I2S_IT_RXNE, ENABLE); // 使能接收中断 SPI_Cmd(SPI2, ENABLE); // 使能SPI2外设 /* NVIC中断控制器配置 */ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 中断优先级分组2 NVIC_InitStructure.NVIC_IRQChannel = SPI2_IRQn; // SPI2中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1; // 抢占优先级3 NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; // 子优先级3 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // IRQ通道使能 NVIC_Init(&NVIC_InitStructure); // 根据指定的参数初始化VIC寄存器 }

SPI2配置为从模式,全双工,使能接收中断。

验证情况

======003

可见,与我们前面分析的一致,ucSPI2_RxBuf为从机接收自主机的数据;ucSPI1_RxBuf为主机接收自从机的数据。这里发现ucSPI1_RxBuf的所有数组元素都往后移了一个单位,那是因为主机第一次发送数据给从机的时候,从机并没有数据返还给主机,即此时还没有数据存储在ucSPI1_RxBuf[0]中。

代码获取

本例子代码工程可在本公众号聊天界面回复关键词:SPI主从,即可获取链接。

我的微信公众号:嵌入式大杂烩

在这里插入图片描述



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有